package com.mikelouis.test.scala.impatient.chapter17_type_parameterized

import scala.runtime.RichInt

/**
 * Created by yuazhu on 4/28/2015.
 */
object Chapter17 extends App {
  // 1
  {
    class Pair[T, S](val p: (T, S)) {
      def swap() = Pair(p._1, p._2)
    }

    println(new Pair(97, 'a').swap())
  }

  // 2
  {
    class Pair[T](var p: (T, T)) {
      def swap() = (p._2, p._1)
    }

    val p = new Pair(97 -> 'a')
    println(p.swap())
    p.p = 98 -> 'b'
    println(p.swap())
  }

  // 3
  {
    class Pair[T, S](val first: T, val second: S) {
      def swap = new Pair[S, T](second, first)

      def swap(p: Pair[T, S]): Pair[S, T] = new Pair[S, T](p.second, p.first)

      override def toString: String = "(" + first + "," + second + ")"
    }

    val p = new Pair(97, 'a')
    println(p.swap)
    println(p.swap(new Pair(98, 'b')))
  }

  // 4
  {
    class Person(val name: String) {
      override def toString = getClass.getName + " " + name
    }
    class Student(name: String) extends Person(name)

    class Pair[T, S](val first: T, val second: S) {
      def swap(p: Pair[T, S]): Pair[S, T] = new Pair[S, T](p.second, p.first)

      // 因为Student是Pair的子类（型），所以不需要给replaceFirst方法定一个下界即可把Pair[Person]的第一个组件替换成Student
      def replaceFirst(newFirst: T) = new Pair[T, S](newFirst, second)

      override def toString: String = "(" + first + "," + second + ")"
    }

    val p1 = new Person("p1")
    val p2 = new Person("p2")
    val s1 = new Student("s1")

    val pair1 = new Pair(p1, s1)
    println(pair1)
    val pair2 = pair1.replaceFirst(s1)
    println(pair2)
  }

  // 5
  // RichInt是Int的富包装类，提供了很多便捷方法，其目的不是为了取代Int而是增强Int
  // 而接口 Comparable 位于 java.lang包。实现  Comparable[Int] 比 实现 Comparable[RichInt] 通用得多
  {
    val a = new RichInt(1)
    val b = new RichInt(2)
    val c = 3
    //println ( a max b) //type mismatch; found : scala.runtime.RichInt required: Int
    println(a max c)
  }

  // 6
  {
    def middle[T](it: Iterable[T]): T = {
      def list = it.toList
      list(list.size / 2)
    }

    println("%s's middle is %c".format("World", middle("World")))
  }

  // 8
  {
    class Person(val name: String) {
      override def toString = getClass.getName + " " + name
    }
    class Student(name: String) extends Person(name)

    class ImmutablePair[+T](val first: T, val second: T) {
      def replaceFirst[R >: T](newFirst: R) = new ImmutablePair[R](newFirst, second)

      override def toString: String = "(" + first + "," + second + ")"
    }

    val p1 = new Person("p1")
    val p2 = new Person("p2")
    val s1 = new Student("s1")
    val s2 = new Student("s2")

    val pair1 = new ImmutablePair(p1, p2)
    println(pair1)
    val pair2 = pair1.replaceFirst(s1)
    println(pair2)

    val pair3 = new ImmutablePair(s1, s2)
    println(pair3)
    val pair4 = pair3.replaceFirst(p1)
    println(pair4)

    // if using MutablePair
    //    Error:(122, 17) type mismatch;
    //    found   : newFirst.type (with underlying type R)
    //    required: T
    //    first = newFirst
    //    ^
    class MutablePair[T](var first: T, var second: T) {
      //      def replaceFirst[R >: T](newFirst: R) = {
      //        first = newFirst
      //        this
      //      }
    }
  }

  // 9
  {
    class NastyPair[+T](val first: T, val second: T) {
      def replaceFirst[R >: T](newFirst: R) = new NastyPair[R](newFirst, second)

      override def toString: String = "(" + first + "," + second + ")"
    }

    class NastyDoublePair(firstC: Double, secondC: Double) extends NastyPair[Double](firstC, secondC) {
      override def replaceFirst[R >: Double](newFirst: R): NastyPair[R] = newFirst match {
        case x: Double => new NastyDoublePair(math.sqrt(x), second)
        case _ => new NastyDoublePair(Double.NaN, second)
      }
    }

    val p: NastyPair[Any] = new NastyDoublePair(4.0, 5.0)
    println(p.replaceFirst("Hello"))
  }

  // 10
  {
    class Pair[S, T](var first: S, var second: T) {
      def swap()(implicit ev: S =:= T, ev2: T =:= S) {
        val tmp = first
        first = second
        second = tmp
      }

      override def toString: String = "(" + first + "," + second + ")"
    }

    val p1 = new Pair(1, 3)
    p1.swap()
    println(p1)

    // definition will not make compile error, only swap called, will have compile error
    val p2 = new Pair(1, "Hello")
    // Error:(169, 12) Cannot prove that Int =:= String.
    //    p2.swap()
    //       ^
//    p2.swap()
    println(p2)
  }

}
